Learn in 10 minutes

Learn in 10 minutes

Go言語を10分で学ぶ

Go(Golangとしても知られる)は、Googleで設計された静的型付けのコンパイル型プログラミング言語です。シンプルさ、効率性、優れた並行性サポートで知られています。このチュートリアルでは、Goプログラミングを素早く学ぶことができます。

1. 最初のGoプログラムを書く

簡単なプログラムから始めましょう。hello.goというファイルを作成し、以下のコードを入力します:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

ファイルを保存し、ターミナルで以下のコマンドを実行します:

go run hello.go

出力は以下のようになります:

Hello, World!

このシンプルなプログラムはGoの基本構造を示しています:

  • package main はパッケージ名を宣言します
  • import "fmt" はI/O操作のためのフォーマットパッケージをインポートします
  • func main() はプログラムのエントリーポイントです
  • fmt.Println() はテキストをコンソールに出力します

2. 基本構文

Goはクリーンでシンプルな構文を持っています。Pythonとは異なり、Goはコードブロックを定義するために中括弧 {} を使用し、文の終わりにセミコロンを必要とします(通常は自動的に挿入されます)。

// これは単一行コメントです
fmt.Println("Hello, World!")

/*
これは複数行コメントです
複数行にまたがっています
*/

Goの基本構文ルール:

  • コードブロック: 中括弧 {} で定義
  • コメント: 単一行コメントは // で開始、複数行は /* */
  • : セミコロンで終了(自動挿入)
  • パッケージ宣言: すべてのファイルはパッケージ宣言で始まります

3. 変数とデータ型

Goは静的型付け言語で、変数の型を宣言する必要があります。ただし、Goは := 演算子による型推論をサポートしています。

変数宣言の方法

// 明示的な型宣言
var name string = "John"
var age int = 25

// 型推論
name := "John"
age := 25

// 複数変数宣言
var x, y int = 10, 20
x, y := 10, 20

Goの主な基本データ型

  • 整数型: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr
  • 浮動小数点型: float32, float64
  • 文字列: string
  • ブール型: bool
  • 複素数型: complex64, complex128

3.1 数値型

Goはさまざまなユースケースに対応する数値型を提供します:

// 整数型
var age int = 25
var smallNumber int8 = 127
var largeNumber int64 = 9223372036854775807

// 浮動小数点型
var temperature float32 = 36.5
var pi float64 = 3.14159265359

// 複素数
var complexNum complex64 = 3 + 4i

3.2 文字列型

Goの文字列はバイトのシーケンスで、不変です:

// 文字列宣言
var greeting string = "Hello, Go!"
name := "Alice"

// 文字列操作
fmt.Println(len(greeting))        // 文字列の長さ
fmt.Println(greeting[0])          // 最初の文字(バイト)にアクセス
fmt.Println(greeting[0:5])        // 文字列スライス
fmt.Println(strings.ToUpper(name)) // 大文字に変換

3.3 ブール型

ブール型は truefalse の2つの値を持ちます:

var isActive bool = true
var isComplete bool = false

// ブール演算
result1 := true && false  // false
result2 := true || false  // true
result3 := !true          // false

4. 定数

定数は const キーワードを使用して宣言され、変更できません:

const Pi = 3.14159
const MaxUsers = 1000

// 複数の定数
const (
    StatusOK = 200
    StatusNotFound = 404
    StatusError = 500
)

// 型付き定数
const Version string = "1.0.0"

5. データ構造

Goはデータの格納と操作のためのいくつかの組み込みデータ構造を提供します。

5.1 配列

配列は同じ型の要素の固定サイズのシーケンスです:

// 配列宣言
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
names := [3]string{"Alice", "Bob", "Charlie"}

// 要素へのアクセス
fmt.Println(numbers[0])  // 1
numbers[0] = 10         // 要素を変更

// 配列の長さ
fmt.Println(len(numbers)) // 5

5.2 スライス

スライスは動的配列で、拡大・縮小が可能です:

// スライス宣言
numbers := []int{1, 2, 3, 4, 5}
var emptySlice []int

// 配列からのスライス作成
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // [2, 3, 4]

// スライス操作
numbers = append(numbers, 6)      // 要素を追加
numbers = append(numbers, 7, 8, 9) // 複数要素を追加

// スライスの容量と長さ
fmt.Println(len(numbers)) // 長さ: 9
fmt.Println(cap(numbers)) // 容量: 10(変動あり)

5.3 マップ

マップはキーと値のペアの順序付けられていないコレクションです:

// マップ宣言
student := map[string]interface{}{
    "name": "John",
    "age":  20,
    "major": "Computer Science",
}

// 代替宣言
var scores map[string]int = make(map[string]int)
scores["math"] = 95
scores["science"] = 88

// アクセスと変更
fmt.Println(student["name"])
student["age"] = 21
student["gpa"] = 3.8

// 安全なアクセス
if phone, exists := student["phone"]; exists {
    fmt.Println(phone)
} else {
    fmt.Println("電話番号が提供されていません")
}

// マップの反復処理
for key, value := range student {
    fmt.Printf("%s: %v\n", key, value)
}

5.4 構造体

構造体は異なる型を持つフィールドのコレクションです:

// 構造体定義
type Person struct {
    Name string
    Age  int
    City string
}

// 構造体インスタンスの作成
person1 := Person{"Alice", 25, "New York"}
person2 := Person{
    Name: "Bob",
    Age:  30,
    City: "London",
}

// フィールドへのアクセス
fmt.Println(person1.Name)
person1.Age = 26

6. 演算と演算子

Goはさまざまな計算と比較のための豊富な演算子セットを提供します。

  • 算術演算子: +, -, *, /, %(剰余)
  • 比較演算子: ==, !=, >, <, >=, <=
  • 論理演算子: &&, ||, !
  • ビット演算子: &, |, ^, <<, >>
  • 代入演算子: =, +=, -=, *=, /=

6.1 算術演算子

a, b := 10, 3

fmt.Printf("加算: %d\n", a + b)      // 13
fmt.Printf("減算: %d\n", a - b)   // 7
fmt.Printf("乗算: %d\n", a * b)  // 30
fmt.Printf("除算: %d\n", a / b)      // 3
fmt.Printf("剰余: %d\n", a % b)      // 1

6.2 比較演算子

x, y := 5, 10

fmt.Printf("等しい: %t\n", x == y)     // false
fmt.Printf("等しくない: %t\n", x != y) // true
fmt.Printf("より大きい: %t\n", x > y)  // false
fmt.Printf("より小さい: %t\n", x < y)  // true

6.3 論理演算子

a, b := true, false

fmt.Printf("AND演算: %t\n", a && b)  // false
fmt.Printf("OR演算: %t\n", a || b)    // true
fmt.Printf("NOT演算: %t\n", !a)    // false

7. 制御フロー

Goはプログラム実行を管理するためのいくつかの制御フロー文を提供します。

7.1 if文

age := 20
if age >= 18 {
    fmt.Println("成人")
} else if age >= 13 {
    fmt.Println("ティーン")
} else {
    fmt.Println("子供")
}

// 短い文を含むif
if score := 85; score >= 90 {
    fmt.Println("成績: A")
} else if score >= 80 {
    fmt.Println("成績: B")
} else {
    fmt.Println("成績: C")
}

7.2 forループ

Goには1つのループ構造しかありません:for

// 基本的なforループ
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// Whileスタイルのループ
count := 0
for count < 5 {
    fmt.Println(count)
    count++
}

// 無限ループ
for {
    fmt.Println("これは永遠に実行されます")
    break // breakで終了
}

// Rangeループ(スライス、配列、マップ用)
fruits := []string{"apple", "banana", "cherry"}
for index, fruit := range fruits {
    fmt.Printf("%d: %s\n", index, fruit)
}

7.3 switch文

day := "Monday"
switch day {
case "Monday":
    fmt.Println("週の始まり")
case "Friday":
    fmt.Println("週末が近い")
case "Saturday", "Sunday":
    fmt.Println("週末!")
default:
    fmt.Println("通常の日")
}

// 式なしのswitch
score := 85
switch {
case score >= 90:
    fmt.Println("成績: A")
case score >= 80:
    fmt.Println("成績: B")
case score >= 70:
    fmt.Println("成績: C")
default:
    fmt.Println("成績: F")
}

8. 関数

Goの関数は第一級市民であり、複数の戻り値をサポートします。

8.1 基本関数

func greet(name string) string {
    return "Hello, " + name + "!"
}

// 関数の呼び出し
message := greet("John")
fmt.Println(message)

8.2 複数の戻り値

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("ゼロで除算できません")
    }
    return a / b, nil
}

// 複数の戻り値の使用
result, err := divide(10, 2)
if err != nil {
    fmt.Println("エラー:", err)
} else {
    fmt.Println("結果:", result)
}

8.3 名前付き戻り値

func calculateRectangle(width, height float64) (area float64, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return // 裸のreturn
}

area, perimeter := calculateRectangle(5, 3)
fmt.Printf("面積: %.2f, 周長: %.2f\n", area, perimeter)

8.4 可変長引数関数

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

fmt.Println(sum(1, 2, 3, 4))  // 10
fmt.Println(sum(5, 10, 15))   // 30

9. ポインタ

Goにはポインタがありますが、C/C++よりもシンプルな構文です:

func modifyValue(x *int) {
    *x = *x * 2
}

func main() {
    value := 10
    fmt.Println("変更前:", value) // 10

    modifyValue(&value)
    fmt.Println("変更後:", value)  // 20
}

10. メソッド

メソッドはレシーバ引数を持つ関数です:

type Rectangle struct {
    Width  float64
    Height float64
}

// 値レシーバを持つメソッド
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// ポインタレシーバを持つメソッド
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{Width: 5, Height: 3}
fmt.Println("面積:", rect.Area()) // 15

rect.Scale(2)
fmt.Println("拡大後の面積:", rect.Area()) // 60

11. インターフェース

インターフェースは型が実装できるメソッドシグネチャを定義します:

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

func printShapeInfo(s Shape) {
    fmt.Printf("面積: %.2f, 周長: %.2f\n", s.Area(), s.Perimeter())
}

circle := Circle{Radius: 5}
printShapeInfo(circle)

12. エラーハンドリング

Goは例外ではなく明示的なエラーハンドリングを使用します:

func readFile(filename string) (string, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return "", fmt.Errorf("ファイル %s の読み込みに失敗しました: %w", filename, err)
    }
    return string(data), nil
}

content, err := readFile("example.txt")
if err != nil {
    fmt.Println("エラー:", err)
    return
}
fmt.Println("内容:", content)

13. ゴルーチンによる並行性

ゴルーチンはGoランタイムによって管理される軽量スレッドです:

func worker(id int) {
    for i := 0; i < 3; i++ {
        fmt.Printf("ワーカー %d: %d\n", id, i)
        time.Sleep(time.Millisecond * 100)
    }
}

func main() {
    // 複数のゴルーチンを開始
    for i := 1; i <= 3; i++ {
        go worker(i)
    }

    // ゴルーチンの完了を待機
    time.Sleep(time.Second)
    fmt.Println("すべてのワーカーが完了しました")
}

14. チャネル

チャネルはゴルーチン間の通信に使用されます:

func producer(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i // チャネルに値を送信
        time.Sleep(time.Millisecond * 100)
    }
    close(ch) // 完了時にチャネルを閉じる
}

func consumer(ch <-chan int) {
    for value := range ch {
        fmt.Println("受信:", value)
    }
}

func main() {
    ch := make(chan int, 3) // バッファ付きチャネル

    go producer(ch)
    consumer(ch)

    fmt.Println("チャネル通信が完了しました")
}

15. ファイル操作

Goはファイルの読み書きのためのシンプルなメソッドを提供します:

// ファイルの読み込み
data, err := os.ReadFile("example.txt")
if err != nil {
    fmt.Println("ファイル読み込みエラー:", err)
    return
}
fmt.Println("ファイル内容:", string(data))

// ファイルの書き込み
content := "Hello, Go!\n"
err = os.WriteFile("output.txt", []byte(content), 0644)
if err != nil {
    fmt.Println("ファイル書き込みエラー:", err)
    return
}
fmt.Println("ファイルが正常に書き込まれました")

16. パッケージとモジュール

Goモジュールは依存関係とパッケージバージョンを管理します:

// 標準ライブラリパッケージのインポート
import (
    "fmt"
    "math"
    "strings"
)

func main() {
    fmt.Println(math.Sqrt(16))        // 4
    fmt.Println(strings.ToUpper("go")) // GO
}

独自のパッケージを作成するには、パッケージ名のディレクトリを作成し、関数名を大文字にしてエクスポートします。

17. テスト

Goには組み込みのテストサポートがあります:

// math_test.goファイル内
package main

import "testing"

func TestAdd(t *testing.T) {
    result := add(2, 3)
    expected := 5
    if result != expected {
        t.Errorf("add(2, 3) = %d; 期待値 %d", result, expected)
    }
}

func add(a, b int) int {
    return a + b
}

テストを実行するには:go test

18. ベストプラクティス

  • gofmt を使用してコードをフォーマットする
  • Goの命名規則に従う(変数はcamelCase、エクスポートはPascalCase)
  • エラーを明示的に処理する
  • 抽象化のためにインターフェースを使用する
  • 継承よりもコンポジションを優先する
  • 包括的なテストを書く
  • 可能な限り標準ライブラリを使用する

このチュートリアルはGoプログラミングの基本的な機能をカバーしています。練習を重ねることで、Goの強力な機能を使用して効率的で並行性のあるアプリケーションを構築できるようになります。